// 版权所有2009 Go作者。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// Solaris系统调用。
// 该文件被编译为普通的Go代码，
// 但它也是mksyscall的输入，
// 它解析
// 请注意，有时我们使用小写
// syscall_solaris中。go或syscall_unix。去

package syscall

import "unsafe"

const _F_DUP2FD_CLOEXEC = F_DUP2FD_CLOEXEC

// 在asm_solaris_amd64中实现。s、 
func rawSysvicall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)
func sysvicall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)

type SockaddrDatalink struct {
	Family uint16
	Index  uint16
	Type   uint8
	Nlen   uint8
	Alen   uint8
	Slen   uint8
	Data   [244]int8
	raw    RawSockaddrDatalink
}

func direntIno(buf []byte) (uint64, bool) {
	return readInt(buf, unsafe.Offsetof(Dirent{}.Ino), unsafe.Sizeof(Dirent{}.Ino))
}

func direntReclen(buf []byte) (uint64, bool) {
	return readInt(buf, unsafe.Offsetof(Dirent{}.Reclen), unsafe.Sizeof(Dirent{}.Reclen))
}

func direntNamlen(buf []byte) (uint64, bool) {
	reclen, ok := direntReclen(buf)
	if !ok {
		return 0, false
	}
	return reclen - uint64(unsafe.Offsetof(Dirent{}.Name)), true
}

func pipe() (r uintptr, w uintptr, err uintptr)

func Pipe(p []int) (err error) {
	if len(p) != 2 {
		return EINVAL
	}
	r0, w0, e1 := pipe()
	if e1 != 0 {
		err = Errno(e1)
	}
	if err == nil {
		p[0], p[1] = int(r0), int(w0)
	}
	return
}

func (sa *SockaddrInet4) sockaddr() (unsafe.Pointer, _Socklen, error) {
	if sa.Port < 0 || sa.Port > 0xFFFF {
		return nil, 0, EINVAL
	}
	sa.raw.Family = AF_INET
	p := (*[2]byte)(unsafe.Pointer(&sa.raw.Port))
	p[0] = byte(sa.Port >> 8)
	p[1] = byte(sa.Port)
	sa.raw.Addr = sa.Addr
	return unsafe.Pointer(&sa.raw), SizeofSockaddrInet4, nil
}

func (sa *SockaddrInet6) sockaddr() (unsafe.Pointer, _Socklen, error) {
	if sa.Port < 0 || sa.Port > 0xFFFF {
		return nil, 0, EINVAL
	}
	sa.raw.Family = AF_INET6
	p := (*[2]byte)(unsafe.Pointer(&sa.raw.Port))
	p[0] = byte(sa.Port >> 8)
	p[1] = byte(sa.Port)
	sa.raw.Scope_id = sa.ZoneId
	sa.raw.Addr = sa.Addr
	return unsafe.Pointer(&sa.raw), SizeofSockaddrInet6, nil
}

func (sa *SockaddrUnix) sockaddr() (unsafe.Pointer, _Socklen, error) {
	name := sa.Name
	n := len(name)
	if n >= len(sa.raw.Path) {
		return nil, 0, EINVAL
	}
	sa.raw.Family = AF_UNIX
	for i := 0; i < n; i++ {
		sa.raw.Path[i] = int8(name[i])
	}
	// 长度是家族（uint16），名称，NUL。
	sl := _Socklen(2)
	if n > 0 {
		sl += _Socklen(n) + 1
	}
	if sa.raw.Path[0] == '@' {
		sa.raw.Path[0] = 0
		// 不计算抽象地址的尾随NUL。
		sl--
	}

	return unsafe.Pointer(&sa.raw), sl, nil
}

func Getsockname(fd int) (sa Sockaddr, err error) {
	var rsa RawSockaddrAny
	var len _Socklen = SizeofSockaddrAny
	if err = getsockname(fd, &rsa, &len); err != nil {
		return
	}
	return anyToSockaddr(&rsa)
}

const ImplementsGetwd = true

// sys Getcwd（buf[]字节）（n int，error error）

func Getwd() (wd string, err error) {
	var buf [PathMax]byte
	// Getcwd如果由于任何原因失败，将返回一个错误。
	_, err = Getcwd(buf[0:])
	if err != nil {
		return "", err
	}
	n := clen(buf[:])
	if n < 1 {
		return "", EINVAL
	}
	return string(buf[:n]), nil
}

/*
 * Wrapped
 */

// sysnb getgroups（ngid int，gid*_gid_t）（n int，error error）
// sysnb setgroups（ngid int，gid*_gid_t）（error）

func Getgroups() (gids []int, err error) {
	n, err := getgroups(0, nil)
	if err != nil {
		return nil, err
	}
	if n == 0 {
		return nil, nil
	}

	// Sanity check group count。麦克斯在BSD上只有16岁。
	if n < 0 || n > 1000 {
		return nil, EINVAL
	}

	a := make([]_Gid_t, n)
	n, err = getgroups(n, &a[0])
	if err != nil {
		return nil, err
	}
	gids = make([]int, n)
	for i, v := range a[0:n] {
		gids[i] = int(v)
	}
	return
}

func Setgroups(gids []int) (err error) {
	if len(gids) == 0 {
		return setgroups(0, nil)
	}

	a := make([]_Gid_t, len(gids))
	for i, v := range gids {
		a[i] = _Gid_t(v)
	}
	return setgroups(len(a), &a[0])
}

func ReadDirent(fd int, buf []byte) (n int, err error) {
	// 最后一个参数是（basep*uintpttr），系统调用不接受nil。
	// TODO（rsc）：我们能为所有通话使用一个全局basep吗？
	return Getdents(fd, buf, new(uintptr))
}

// 等待状态是底部的7位，要么是0（已退出），要么是导致退出的信号号。
// 0x80位表示是否存在内核转储。
// 一个额外的数字（退出代码、导致停止的信号）
// 位于高位。

type WaitStatus uint32

const (
	mask  = 0x7F
	core  = 0x80
	shift = 8

	exited  = 0
	stopped = 0x7F
)

func (w WaitStatus) Exited() bool { return w&mask == exited }

func (w WaitStatus) ExitStatus() int {
	if w&mask != exited {
		return -1
	}
	return int(w >> shift)
}

func (w WaitStatus) Signaled() bool { return w&mask != stopped && w&mask != 0 }

func (w WaitStatus) Signal() Signal {
	sig := Signal(w & mask)
	if sig == stopped || sig == 0 {
		return -1
	}
	return sig
}

func (w WaitStatus) CoreDump() bool { return w.Signaled() && w&core != 0 }

func (w WaitStatus) Stopped() bool { return w&mask == stopped && Signal(w>>shift) != SIGSTOP }

func (w WaitStatus) Continued() bool { return w&mask == stopped && Signal(w>>shift) == SIGSTOP }

func (w WaitStatus) StopSignal() Signal {
	if !w.Stopped() {
		return -1
	}
	return Signal(w>>shift) & 0xFF
}

func (w WaitStatus) TrapCause() int { return -1 }

func wait4(pid uintptr, wstatus *WaitStatus, options uintptr, rusage *Rusage) (wpid uintptr, err uintptr)

func Wait4(pid int, wstatus *WaitStatus, options int, rusage *Rusage) (wpid int, err error) {
	r0, e1 := wait4(uintptr(pid), wstatus, uintptr(options), rusage)
	if e1 != 0 {
		err = Errno(e1)
	}
	return int(r0), err
}

func gethostname() (name string, err uintptr)

func Gethostname() (name string, err error) {
	name, e1 := gethostname()
	if e1 != 0 {
		err = Errno(e1)
	}
	return name, err
}

func UtimesNano(path string, ts []Timespec) error {
	if len(ts) != 2 {
		return EINVAL
	}
	return utimensat(_AT_FDCWD, path, (*[2]Timespec)(unsafe.Pointer(&ts[0])), 0)
}

// sys fcntl（fd int，cmd int，arg int）（val int，err error）

// FcntlFlock为F_GETLK、F_SETLK或F_SETLKW命令执行fcntl系统调用。
func FcntlFlock(fd uintptr, cmd int, lk *Flock_t) error {
	_, _, e1 := sysvicall6(uintptr(unsafe.Pointer(&libc_fcntl)), 3, uintptr(fd), uintptr(cmd), uintptr(unsafe.Pointer(lk)), 0, 0, 0)
	if e1 != 0 {
		return e1
	}
	return nil
}

func anyToSockaddr(rsa *RawSockaddrAny) (Sockaddr, error) {
	switch rsa.Addr.Family {
	case AF_UNIX:
		pp := (*RawSockaddrUnix)(unsafe.Pointer(rsa))
		sa := new(SockaddrUnix)
		// 假设路径以NUL结束。
		// 从技术上讲，这并不是
		// 抽象Unix域套接字的Solaris语义——它们应该是未经解释的固定大小二进制Blob——但
		// 每个人都使用此约定。
		n := 0
		for n < len(pp.Path) && pp.Path[n] != 0 {
			n++
		}
		bytes := (*[len(pp.Path)]byte)(unsafe.Pointer(&pp.Path[0]))[0:n]
		sa.Name = string(bytes)
		return sa, nil

	case AF_INET:
		pp := (*RawSockaddrInet4)(unsafe.Pointer(rsa))
		sa := new(SockaddrInet4)
		p := (*[2]byte)(unsafe.Pointer(&pp.Port))
		sa.Port = int(p[0])<<8 + int(p[1])
		sa.Addr = pp.Addr
		return sa, nil

	case AF_INET6:
		pp := (*RawSockaddrInet6)(unsafe.Pointer(rsa))
		sa := new(SockaddrInet6)
		p := (*[2]byte)(unsafe.Pointer(&pp.Port))
		sa.Port = int(p[0])<<8 + int(p[1])
		sa.ZoneId = pp.Scope_id
		sa.Addr = pp.Addr
		return sa, nil
	}
	return nil, EAFNOSUPPORT
}

// sys accept（s int，rsa*RawSockaddrAny，addrlen*_Socklen）（fd int，err error）=libsocket。接受

func Accept(fd int) (nfd int, sa Sockaddr, err error) {
	var rsa RawSockaddrAny
	var len _Socklen = SizeofSockaddrAny
	nfd, err = accept(fd, &rsa, &len)
	if err != nil {
		return
	}
	sa, err = anyToSockaddr(&rsa)
	if err != nil {
		Close(nfd)
		nfd = 0
	}
	return
}

func recvmsgRaw(fd int, p, oob []byte, flags int, rsa *RawSockaddrAny) (n, oobn int, recvflags int, err error) {
	var msg Msghdr
	msg.Name = (*byte)(unsafe.Pointer(rsa))
	msg.Namelen = uint32(SizeofSockaddrAny)
	var iov Iovec
	if len(p) > 0 {
		iov.Base = (*int8)(unsafe.Pointer(&p[0]))
		iov.SetLen(len(p))
	}
	var dummy int8
	if len(oob) > 0 {
		// 至少接收一个正常字节
		if len(p) == 0 {
			iov.Base = &dummy
			iov.SetLen(1)
		}
		msg.Accrights = (*int8)(unsafe.Pointer(&oob[0]))
		msg.Accrightslen = int32(len(oob))
	}
	msg.Iov = &iov
	msg.Iovlen = 1
	if n, err = recvmsg(fd, &msg, flags); err != nil {
		return
	}
	oobn = int(msg.Accrightslen)
	return
}

// sys sendmsg（s int，msg*Msghdr，flags int）（n int，err error error）=libsocket__xnet_sendmsg 

func sendmsgN(fd int, p, oob []byte, ptr unsafe.Pointer, salen _Socklen, flags int) (n int, err error) {
	var msg Msghdr
	msg.Name = (*byte)(unsafe.Pointer(ptr))
	msg.Namelen = uint32(salen)
	var iov Iovec
	if len(p) > 0 {
		iov.Base = (*int8)(unsafe.Pointer(&p[0]))
		iov.SetLen(len(p))
	}
	var dummy int8
	if len(oob) > 0 {
		// 发送至少一个正常字节
		if len(p) == 0 {
			iov.Base = &dummy
			iov.SetLen(1)
		}
		msg.Accrights = (*int8)(unsafe.Pointer(&oob[0]))
		msg.Accrightslen = int32(len(oob))
	}
	msg.Iov = &iov
	msg.Iovlen = 1
	if n, err = sendmsg(fd, &msg, flags); err != nil {
		return 0, err
	}
	if len(oob) > 0 && len(p) == 0 {
		n = 0
	}
	return n, nil
}

/*
 * Exposed directly
 */
// sys Access（路径字符串，模式uint32）（错误）
// sys Adjtime（delta*Timeval，olddta*Timeval）（错误）
// sys Chdir（路径字符串）（错误）
// sys Chmod（路径字符串，模式uint32）（错误）
// sys Close（fd int）（err error）
// sys Dup（fd int）（nfd int，err error）
// sys Fchdir（fd int）（err err error）
// sys Fchmod（fd int，mode uint32）（err err error）
// sys Fchown（fd int，uid，gid int）
// sys Fstat（fd int，stat*stat_t）（error error）
// sys Getdents（fd int，buf[]字节，basep*uintptr）（n int，error error）
// sysnb Getgid（）（gid int）
// sys Getpriority（which int，who int）（n int，error error）
// sysnb Getrlimit（which int，lim*Rlimit）（error error）
// sysnb Gettimeofday（tv*Timeval）（error error）
// sys Kill（pid int，signum signum Signal）（error）
// sys Link（路径字符串，链接字符串）（错误）
// sys Listen（s int，backlog int）（错误）=libsocket__xnet_listen 
// sys Lstat（路径字符串，stat*stat_t）（错误）
// sys Mkdir（路径字符串，模式uint32）（错误）
// sys Mknod（路径字符串，模式uint32，开发int）（错误）
// sys Nanosleep（时间*时间段，剩余时间*时间段）（错误）
// sys Pread（fd int，p[]字节，offset int64）（n int，error error）
// sys Rmdir（路径字符串）（错误）
// sys Seek（fd int，offset int64，whence int）（newoffset int64，err error）=lseek 
// /sys sendfile（outpd int，infd int，offset*int64，count int）（writed int，err error）=libsendfile。sendfile 
// sysnb Setgid（egid int）（错误）
// sysnb Seteuid（euid int）（错误）
// sysnb Setgid（gid int）（错误）
// sysnb Setpgid（pid int，pgid int）（错误）
// sysnb Setrlimit（which int，lim*Rlimit）（error error）
// sysnb setid（）（pid int，error error）
// sysnb Setuid（uid int）（error error）
// sys Shutdown（s int，how int）（error error）=libsocket。关机
// sys Stat（路径字符串，Stat*Stat_t）（错误）
// sys Symlink（路径字符串，链接字符串）（错误）
// sys Sync（）（错误）
// sys Truncate（路径字符串，长度int64）（错误）
// sys Umask（newmask int）（oldmask int）
// sys Unlink（path string）（err error）
// sys utimes（path string，times*[2]Timeval）（error error）
// sys bind（s int，addr unsafe.Pointer，addrlen_Socklen）（errr error）=libsocket__xnet_bind 
// sys connect（s int，addr unsafe.Pointer，addrlen _Socklen）（err error）=libsocket__xnet_connect 
// sys mmap（addr uintpttr，length uintpttr，prot int，flag int，fd int，pos int64）（ret uintptpr，err error）
// sys munmap（addr uintpr，length uintpr）（error error）
// sys socket（domain int，typ int，proto int）（fd int，err error）=libsocket__xnet_socket 
// sysnb socketpair（域int，类型int，proto int，fd*[2]int32）（错误）=libsocket__xnet_socketpair 
// sys write（fd int，p[]字节）（n int，error error）
// sys getsockopt（s int，level int，name int，val unsafe.Pointer，vallen*_Socklen）（error error）=libsocket__xnet_getsockopt 
// sysnb getpeername（fd int，rsa*RawSockaddrAny，addrlen*_Socklen）（err error）=libsocket。getpeername 
// sys getsockname（fd int，rsa*RawSockaddrAny，addrlen*_Socklen）（错误）=libsocket。getsockname 
// sys setsockopt（s int，level int，name int，val unsafe.Pointer，vallen uintptpr）（err error）=libsocket。setsockopt 
// sys recvfrom（fd int，p[]字节，flags int，from*RawSockaddrAny，fromlen*_Socklen）（n int，err error）=libsocket。recvfrom 
// sys recvmsg（s int，msg*Msghdr，flags int）（n int，err error error）=libsocket__xnet_recvmsg 
// sys getexecname（）（路径不安全。指针，错误）=libc。getexecname 
// sys utimensat（dirfd int，path string，times*[2]Timespec，flag int）（错误）

func Getexecname() (path string, err error) {
	ptr, err := getexecname()
	if err != nil {
		return "", err
	}
	bytes := (*[1 << 29]byte)(ptr)[:]
	for i, b := range bytes {
		if b == 0 {
			return string(bytes[:i]), nil
		}
	}
	panic("unreachable")
}

func readlen(fd int, buf *byte, nbuf int) (n int, err error) {
	r0, _, e1 := sysvicall6(uintptr(unsafe.Pointer(&libc_read)), 3, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf), 0, 0, 0)
	n = int(r0)
	if e1 != 0 {
		err = e1
	}
	return
}

func writelen(fd int, buf *byte, nbuf int) (n int, err error) {
	r0, _, e1 := sysvicall6(uintptr(unsafe.Pointer(&libc_write)), 3, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf), 0, 0, 0)
	n = int(r0)
	if e1 != 0 {
		err = e1
	}
	return
}

func Utimes(path string, tv []Timeval) error {
	if len(tv) != 2 {
		return EINVAL
	}
	return utimes(path, (*[2]Timeval)(unsafe.Pointer(&tv[0])))
}
